// config/redis.js
import { createClient } from 'redis'
const { NODE_ENV, REDIS_HOST, REDIS_PORT, REDIS_PASSWORD } = process.env

class RedisTool {
  constructor() {
    // 合并配置参数
    this.config = {
      socket: {
        host: REDIS_HOST || 'localhost',
        port: Number(REDIS_PORT) || 6379,
        tls: NODE_ENV === 'production',
        connectTimeout: 5000
      },
      password: REDIS_PASSWORD || undefined,
      pingInterval: 30000,
      disableOfflineQueue: true
    }

    // 创建客户端实例
    this.client = createClient(this.config)

    // 绑定事件监听
    this._bindEvents()
  }

  // 事件监听处理
  _bindEvents() {
    // 开发环境日志
    if (NODE_ENV !== 'production') {
      this.client.on('connect', () =>
        console.log('✅ Redis 连接成功'))

      this.client.on('reconnecting', () =>
        console.log('🔄 Redis 重连中...'))
    }

    // 错误处理
    this.client.on('error', (err) => {
      console.error(`❌ Redis错误: ${err.code}`, err.message)
      if (NODE_ENV === 'production' && err.code === 'ECONNREFUSED') {
        process.exit(1)
      }
    })
  }

  // 初始化连接（带超时控制）
  async init() {
    try {
      await Promise.race([
        this.client.connect(),
        new Promise((_, reject) =>
          setTimeout(() => reject(new Error('连接超时')), 6000)
        )
      ])
    } catch (err) {
      console.error('Redis初始化失败:', err.message)
      if (NODE_ENV === 'production') throw err
    }
  }


  // -------------------- 基础操作 --------------------
  async set(key, value, ttl) {
    const serialized = JSON.stringify(value)
    return ttl ?
      this.client.setEx(key, ttl, serialized) :
      this.client.set(key, serialized)
  }

  async get(key) {
    const data = await this.client.get(key)
    return data ? JSON.parse(data) : null
  }

  async del(key) {
    return this.client.del(key)
  }

  // -------------------- 高级操作 --------------------
  async hSet(key, field, value) {
    return this.client.hSet(key, field, JSON.stringify(value))
  }

  async hGetAll(key) {
    const data = await this.client.hGetAll(key)
    return Object.entries(data).reduce((acc, [k, v]) => {
      acc[k] = JSON.parse(v)
      return acc
    }, {})
  }
  async incr(key) {
    return await this.client.incr(key)
  }

  // -------------------- 连接管理 --------------------
  async disconnect() {
    await this.client.quit()
    console.log('Redis连接已关闭')
  }
  async expire(key, value) {
    return await this.client.expire(key, value)
  }
  async exists(key) {
    return await this.client.exists(key)
  }
  multi() {
    return this.client.multi(); // 必须带括号执行
  }
  /**
   * 通用命令执行方法（支持所有 Redis 命令）
   * @example 
   * await redis.call('zAdd', 'sortedset', { score: 1, value: 'member' })
   * await redis.call('lRange', 'mylist', 0, -1)
   */
  async call(command, ...args) {
    if (!(command in this.client)) {
      throw new Error(`Redis 命令 ${command} 不存在或不受支持`)
    }
    return this.client[command](...args)
  }

  // 单例模式
  static getInstance() {
    if (!RedisTool.instance) {
      RedisTool.instance = new RedisTool()
      // 非测试环境自动连接
      if (NODE_ENV !== 'test') RedisTool.instance.init()
    }
    return RedisTool.instance
  }
}

// 单例实例 + 优雅关闭
const redis = RedisTool.getInstance()
process.on('SIGINT', () => redis.disconnect())

export default redis